package Gee

import (
	"fmt"
	"log"
	"net/http"
	"runtime"
	"strings"
)

func Recovery() HandlerFunc {
	return func(c *Context) {
		defer func() {
			err := recover()
			if err != nil {
				message := fmt.Sprintf("%s \n", err)
				log.Printf("%s \n", trace(message))
				c.Fail(http.StatusInternalServerError, "Internal Server Error")
			}
		}()
		c.Next()
	}
}

// trace 追踪函数调用栈
func trace(message string) string {
	var pcr [32]uintptr
	//func Callers(skip int, pc []uintptr) int
	//函数把当前go程调用栈上的调用栈标识符填入切片pc中，返回写入到pc中的项数。
	//实参skip为开始在pc中记录之前所要跳过的栈帧数，0表示Callers自身的调用栈，1表示Callers所在的调用栈。
	//返回写入p的项数。
	n := runtime.Callers(3, pcr[:]) // n 最大为32
	var str strings.Builder
	str.WriteString(message + "\nTraceback:")
	for _, pc := range pcr[:n] {
		function := runtime.FuncForPC(pc)
		file, lineNum := function.FileLine(pc)
		str.WriteString(fmt.Sprintf("\n\t %s:%d ", file, lineNum))
	}
	return str.String()
}
